#include <stdexcept>

#include "Rules.h"
#include "RuleCompiler.h"
#include "Operation.h"
#include "Field.h"

#define BITS_PER_LONG	(sizeof(unsigned long) * 8)

/*
 * RuleCompiler
 */

const AbstractRule *RuleCompiler::getRule(const Operation &o)
{
	std::unordered_map<std::string, AbstractRule *>::iterator it;
#ifdef DEBUG
	std::cout << "Looking for rule type " << o.action << std::endl;
#endif
	it = templates.find(o.action);
	if (it == templates.end())
		throw std::runtime_error("Invalid rule type specified.");
	return it->second;
}

void RuleCompiler::compile(AbstractRule &rule, const Operation &o)
{
	rule.compile(*this, o);
}

void RuleCompiler::compile(ComplexRule &rule, const Operation &o)
{
	const std::vector<Operation> &os = o.operations;
	std::vector<Operation>::const_iterator it;
	for (it = os.begin(); it != os.end(); ++it) {
		rule.push_back(getRule(*it)->clone());
		rule.back()->compile(*this, *it);
	}
}

void RuleCompiler::compile(ProxyRule &rule, const Operation &o)
{
	const std::vector<Operation> &os = o.operations;
	if (os.empty())
		throw std::runtime_error("Empty operation vector given.");
	rule.set(getRule(os.front())->clone());
	rule.get()->compile(*this, os.front());
}

void RuleCompiler::compile(EqualsRule &rule, const Operation &o)
{
	const std::vector<Operation> &os = o.operations;
	std::vector<Operation>::const_iterator it;
	for (it = os.begin(); it != os.end(); ++it) {
		if (it->action == "fieldname")
			break;
	}
	if (it == os.end())
		throw std::runtime_error("No field specified for a terminal rule.");

	std::unordered_map<std::string, RuleField>::const_iterator fit;
#ifdef DEBUG
	std::cout << "Compiling a rule for field " << it->operand.fieldname << std::endl;
#endif
	fit = fields.find(it->operand.fieldname);
	if (fit == fields.end())
		throw std::runtime_error("Invalid field specified in a rule.");

	const RuleField &f = fit->second;
	rule.isTcp = f.isTcp;
	rule.offset = f.word;
	if (f.bit + f.length < BITS_PER_LONG)
		rule.mask = (1UL << (f.bit + f.length)) - 1;
	else
		rule.mask = (unsigned long)-1;
	rule.mask &= ~((1UL << f.bit) - 1);
	rule.value = ((unsigned long)it->operand.value << f.bit) & rule.mask;
#ifdef DEBUG
	std::cout << "Offset: " << rule.offset << ", mask: " << std::hex
		<< rule.mask << ", value: " << rule.value << std::dec << std::endl;
#endif
}

void RuleCompiler::compile(DifferRule &rule, const Operation &o)
{
	const std::vector<Operation> &os = o.operations;
	std::vector<Operation>::const_iterator it;
	for (it = os.begin(); it != os.end(); ++it) {
		if (it->action == "fieldname")
			break;
	}
	if (it == os.end())
		throw std::runtime_error("No field specified for a terminal rule.");

	std::unordered_map<std::string, RuleField>::const_iterator fit;
#ifdef DEBUG
	std::cout << "Compiling a rule for field " << it->operand.fieldname << std::endl;
#endif
	fit = fields.find(it->operand.fieldname);
	if (fit == fields.end())
		throw std::runtime_error("Invalid field specified in a rule.");

	const RuleField &f = fit->second;
	rule.isTcp = f.isTcp;
	rule.offset = f.word;
	if (f.bit + f.length < BITS_PER_LONG)
		rule.mask = (1UL << (f.bit + f.length)) - 1;
	else
		rule.mask = (unsigned long)-1;
	rule.mask &= ~((1UL << f.bit) - 1);
	rule.delta = ((unsigned long)it->operand.value << f.bit) & rule.mask;
#ifdef DEBUG
	std::cout << "Offset: " << rule.offset << ", mask: " << std::hex
		<< rule.mask << ", delta: " << rule.delta << std::dec << std::endl;
#endif
}

RuleCompiler &RuleCompiler::getInstance(void)
{
	static RuleCompiler instance;
	return instance;
}

std::pair<size_t, size_t> RuleCompiler::parseFields(const std::vector<Field> &f)
{
	unsigned ipSize = 0, tcpSize = 0;
	std::vector<Field>::const_iterator it;
	fields.clear();
	for (it = f.begin(); it != f.end(); ++it) {
		bool tcp = (it->header == "tcp");
		fields[it->name] = RuleField(it->offset / BITS_PER_LONG,
				it->offset % BITS_PER_LONG, it->length, tcp);
#ifdef DEBUG
		std::cout << "Adding field " << it->name << std::endl;
#endif
		unsigned size = it->offset + it->length;
		size += BITS_PER_LONG - 1;
		size &= ~(BITS_PER_LONG - 1);
		size /= 8;
		if (tcp) {
			if (size > tcpSize)
				tcpSize = size;
		} else {
			if (size > ipSize)
				ipSize = size;
		}
	}
	ipSize += sizeof(unsigned long) - 1;
	ipSize &= ~(sizeof(unsigned long) - 1);
	tcpSize += sizeof(unsigned long) - 1;
	tcpSize &= ~(sizeof(unsigned long) - 1);
	return std::make_pair(ipSize, tcpSize);
}

RuleCompiler::RuleCompiler()
{
	templates["and"] = new ComplexANDRule;
	templates["or"] = new ComplexORRule;
	templates["not"] = new NOTRule;
	templates["equals"] = new EqualsRule;
	templates["differ"] = new DifferRule;
}

RuleCompiler::~RuleCompiler()
{
	std::unordered_map<std::string, AbstractRule *>::iterator it;
	for (it = templates.begin(); it != templates.end(); ++it)
		delete it->second;
	templates.clear();
}
